﻿using System;
using System.IO;
using System.Net.Sockets;

namespace Pfz.Remoting
{
	/// <summary>
	/// This class represents the Client returned by DisposableTcpListener.
	/// It can also be used to wrap TcpClient objects to the IClient interface.
	/// </summary>
	public sealed class DisposableTcpConnection:
		IConnection
	{
		internal TcpClient _client;

		#if !SILVERLIGHT
			private DisposableTcpListener _listener;

			internal DisposableTcpConnection(DisposableTcpListener listener, TcpClient client, Stream stream)
			{
				_listener = listener;
				_client = client;
				_stream = stream;

				lock(_listener._lock)
					_listener._clients.Add(this);
			}
		#endif

		/// <summary>
		/// Creates a new DisposableTcpClient over the given TcpClient.
		/// </summary>
		public DisposableTcpConnection(TcpClient client)
		{
			if (client == null)
				throw new ArgumentNullException("client");

			_client = client;
			_stream = client.GetStream();
		}

		/// <summary>
		/// Creates a new DisposableTcpClient connecting to the given host and port and allowing you to
		/// set if tcp delay is used or not.
		/// </summary>
		public DisposableTcpConnection(string hostname, int port, bool useTcpDelay, int bufferedWriteStreamSize=-1):
			this(new TcpClient(hostname, port))
		{
			#if !SILVERLIGHT
				_client.NoDelay = !useTcpDelay;
			#endif

			if (bufferedWriteStreamSize != -1)
				Stream = new BufferedWriteStream(Stream, bufferedWriteStreamSize);
		}

		/// <summary>
		/// Releases the stream and internal client useds, and removes itself from the listener.
		/// </summary>
		public void Dispose()
		{
			Disposer.Dispose(ref _stream);
			Disposer.Dispose(ref _client);

			#if !SILVERLIGHT
				var listener = _listener;
				if (listener != null)
				{
					var clients = listener._clients;
					if (clients != null)
						lock(_listener._lock)
							clients.Remove(this);
				}
			#endif
		}

		/// <summary>
		/// Gets a value indicating if this client was disposed/disconnected.
		/// </summary>
		public bool WasDisposed
		{
			get
			{
				var client = _client;
				return client == null || !client.Connected;
			}
		}

		private Stream _stream;
		/// <summary>
		/// Gets or sets the stream used to communicate.
		/// Even if the stream can be changed, only do that if you are decorating the original stream.
		/// </summary>
		public Stream Stream
		{
			get
			{
				return _stream;
			}
			set
			{
				_stream = value;
			}
		}

		/// <summary>
		/// Gets the local endpoint.
		/// </summary>
		public string LocalEndpoint
		{
			get
			{
				#if SILVERLIGHT
					return "Local... no idea which port";
				#else
					return _client.Client.LocalEndPoint.ToString();
				#endif
			}
		}

		/// <summary>
		/// Gets the remote endpoint.
		/// </summary>
		public string RemoteEndpoint
		{
			get
			{
				#if SILVERLIGHT
					return _client._socket.RemoteEndPoint.ToString();
				#else
					return _client.Client.RemoteEndPoint.ToString();
				#endif
			}
		}
	}
}
